# -*- coding: utf-8 -*-
import base64
from threading import Thread
import time
import requests
import logging
from io import BytesIO
import http.cookiejar as cookielib
from PIL import Image
import os, sys

requests.packages.urllib3.disable_warnings()

headers  = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0'}

logger = logging.getLogger(__name__)

def initloger():
    if not os.path.exists('./logs'):
        os.mkdir('./logs')
    logger.setLevel(logging.DEBUG)
    fh = logging.FileHandler('./logs/{}.log'.format(time.strftime('%Y%m%d', time.localtime(time.time()))), encoding='utf-8',mode='a')
    formatter = logging.Formatter("%(asctime)s - %(message)s", datefmt='%Y-%m-%d %H:%M:%S')
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    sh = logging.StreamHandler()
    sh.setFormatter(formatter)
    logger.addHandler(sh)
initloger()

class showpng(Thread):
    def __init__(self, data):
        Thread.__init__(self)
        self.data = data

    def run(self):
        img = Image.open(BytesIO(self.data))
        img.show()

def islogin(userid):
    if not os.path.exists('./cache'):
        os.mkdir('./cache')
    cookiefile = './cache/%s.txt' % userid
    if not os.path.exists(cookiefile):
        with open(cookiefile, 'w') as f:
            f.write('')
    session = requests.session()
    session.cookies = cookielib.LWPCookieJar(filename=cookiefile)
    try:
        session.cookies.load(ignore_discard=True)
        cookies = getcookies(session)
        api_ph  = cookies['kuaishou.web.cp.api_ph']
    except Exception:
        api_ph  = ''
    res = session.post("https://cp.kuaishou.com/rest/pc/authority/account/current", json={"kuaishou.web.cp.api_ph": api_ph}, verify=False, headers=headers).json()
    if res['result'] == 1:
        return 1, session
    else:
        #logger.info('登录凭证失效，请重新扫码登录！')
        return 0, session

def login(userid, logtype='qrcode'):
    states, session = islogin(userid)
    if states == 0:
        session = requests.session()
        cookiefile = './cache/%s.txt' % userid
        session.cookies = cookielib.LWPCookieJar(filename=cookiefile)
        urldata = session.post('https://id.kuaishou.com/rest/c/infra/ks/qr/start', data={"sid": "kuaishou.web.cp.api"}, headers=headers).json()
        testpng = base64.b64decode(urldata['imageData'])
        token   = urldata['qrLoginToken']
        Signat  = urldata['qrLoginSignature']
        t = showpng(testpng)
        t.start()
        tokenurl = 'https://id.kuaishou.com/rest/c/infra/ks/qr/scanResult'
        while True:
            tokendata = session.post(tokenurl, data={"qrLoginToken": token, "qrLoginSignature": Signat}, headers=headers).json()
            if tokendata['result'] == 707:
                logger.info('登录二维码已过期，请重新登录')
            if tokendata['result'] == 1:
                data  = session.post('https://id.kuaishou.com/rest/c/infra/ks/qr/acceptResult', data={"qrLoginToken": token, "qrLoginSignature": Signat, "sid": "kuaishou.web.cp.api"}, headers=headers)
                data1 = session.post('https://id.kuaishou.com/pass/kuaishou/login/qr/callback', data={"qrToken": data.json()['qrToken'], "sid": "kuaishou.web.cp.api"}, headers=headers)
                session.post('https://www.kuaishou.com/account/login/api/verifyToken', json={"authToken": data1.json()['kuaishou.web.cp.api.at'], "sid": "kuaishou.web.cp.api"}, headers=headers)
                logger.info('已确认，登录成功')
                break
            time.sleep(3)
        session.cookies.save()
    return 1, session

def smslogin(phone):
    data = {
        'sid': 'kuaishou.web.cp.api',
        'type': '53',
        'countryCode': '+86',
        'phone': phone,
        'ztIdentityVerificationType': '',
        'ztIdentityVerificationCheckToken': ''
    }
    res = session.post('https://id.kuaishou.com/pass/kuaishou/sms/requestMobileCode', data=data, headers=headers).json()
    if res['result'] == 1:
        data1 = {
            'sid': 'kuaishou.web.cp.api',
            'countryCode': '+86',
            'createId': 'true',
            'phone': phone,
            'smsCode': code
        }
        req = session.post('https://id.kuaishou.com/pass/kuaishou/login/mobileCode', data=data1, headers=headers).json()
        if req['result'] == 100110007:
            logger.info(req['error_msg'])
        if req['result'] == 100110031:
            multiUserToken = res['multiUserToken']
            targetUserId   = res['userInfos'][1]['userId']
            data2 = {
                'sid': 'kuaishou.web.cp.api',
                'countryCode': '+86',
                'account': '',
                'targetUserId': targetUserId,
                'multiUserToken': multiUserToken,
                'phone': phone,
                'setCookie': 'true'
            }
            session.post('https://id.kuaishou.com/pass/kuaishou/login/multiUserToken', data=data2, headers=headers)
        if req['result'] == 1:
            session.post('https://www.kuaishou.com/account/login/api/verifyToken', json={"authToken": data1.json()['kuaishou.web.cp.api.at'], "sid": "kuaishou.web.cp.api"}, headers=headers)

def getaccount(session):
    cookies = getcookies(session)
    api_ph  = cookies['kuaishou.web.cp.api_ph']
    req = session.post('https://cp.kuaishou.com/rest/pc/authority/account/current', json={"kuaishou.web.cp.api_ph": api_ph}, headers=headers)
    res = req.json()
    if res['result'] == 1:
        userId = res['data']['userId']
        userName = res['data']['userName']
        userAvatar = res['data']['userAvatar']
        return userId, userName, userAvatar
    return False

def getcookies(session):
    cookies = {}
    for ck in session.cookies:
        cookies[ck.name] = ck.value
    return cookies

def uploadtoken(session):
    # 1获取上传token
    cookies = getcookies(session)
    api_ph  = cookies['kuaishou.web.cp.api_ph']
    req = session.post('https://cp.kuaishou.com/rest/cp/works/v2/video/pc/upload/pre', json={"kuaishou.web.cp.api_ph": api_ph, "uploadType": "1"}, headers=headers)
    res = req.json()
    if res['result'] == 1:
        token = res['data']['token']
    else:
        logger.info('获取上传token - ' + res['message'])
    # 2申请上传
    session.get('https://upload.kuaishouzt.com/api/upload/resume?upload_token=%s' % token)
    return token, api_ph

def workslist(phone):
    # 获取已发布视频
    login, session = islogin(phone)
    if login == 0:
        return False

    def convertTime(fmtime):
            return time.mktime(time.strptime(fmtime, "%Y-%m-%d %H:%M:%S"))

    cookies = getcookies(session)
    userId  = cookies['userId']
    api_ph  = cookies['kuaishou.web.cp.api_ph']
    endTime = int(convertTime(time.strftime("%Y-%m-%d 23:59:59", time.localtime(time.time()))) * 1000)
    startTime = endTime - 7*86400*1000 + 1000
    data = {
        'userId':userId,
        'status':1,
        'endTime':endTime,
        'startTime':startTime,
        'dateType':3,
        'count':50,
        'page':1,
        'kuaishou.web.cp.api_ph':api_ph
    }
    res = session.post('https://cp.kuaishou.com/rest/pc/user/works/list', json=data, headers=headers).json()
    if res['result'] == 1:
        return res['data']['works']
        #todo: 翻页处理
        totalCount = res['data']['totalCount']
        pageCount  = int(int(totalCount + 50 - 1) / 50)
        for work in res['data']['works']:
            workId, title, cover, uploadTime, playCount, likeCount, commentCount = '','','','','','',''

def upvideo(session, filePath, title, longitude, latitude):
    uptoken, api_ph = uploadtoken(session)
    with open(filePath, 'rb') as f:
        f.seek(0, 2)
        flen = f.tell()
        f.seek(0, 0)
        i = 0
        while True:
            chunk = f.read(4194304)
            if not chunk:
                break
            # 按4M大小分块上传视频
            upload(session, uptoken, chunk, flen, i)
            i += 1
    try:
        # 校验上传完成
        coverKey, fileId = upload_check(session, uptoken, api_ph, filePath, flen, i)
        # 发布
        result = submit(session, api_ph, coverKey, fileId, title, longitude, latitude)
        logger.info('分块上传成功 - 文件校验通过')
    except Exception as e:
        logger.info('发布出错 - ' + str(e))
        result = 0
    return result

def upload(session, uptoken, chunk, flen, i):
    Content_Range = 'bytes %s-%s/%s' % (str(i*4194304), str((i+1)*4194304-1), str(flen))
    headersx = {
        'Accept':'application/json, text/plain, */*',
        'Accept-Encoding':'gzip, deflate, br',
        'Accept-Language':'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
        'Connection':'keep-alive',
        'Content-Length':str(len(chunk)),
        'Content-Range':Content_Range,
        'Content-Type':'application/octet-stream',
        'Host':'upload.kuaishouzt.com',
        'Origin':'https://cp.kuaishou.com',
        'Referer':'https://cp.kuaishou.com/article/publish/video?origin=www.kuaishou.com',
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0'
    }
    try:
        session.post('https://upload.kuaishouzt.com/api/upload/fragment?upload_token=%s&fragment_id=%s' % (uptoken, i), data=chunk, headers=headersx, stream=True)
    except Exception as e:
        logger.info('分块上传出错 - ' + str(e))

def upload_check(session, uptoken, api_ph, fname, flen, fragment):
    req = session.post('https://upload.kuaishouzt.com/api/upload/complete?fragment_count=%s&upload_token=%s' % (fragment, uptoken), json={}, headers=headers, verify=False)
    res = req.json()
    data = {
        'fileLength': flen,
        'fileName': os.path.basename(fname),
        'fileType': 'video/mp4',
        'kuaishou.web.cp.api_ph': api_ph,
        'token': uptoken
    }
    req = session.post('https://cp.kuaishou.com/rest/cp/works/v2/video/pc/upload/finish', json=data, headers=headers)
    #print(req.text)
    res = req.json()
    if res['result'] == 1:
        return res['data']['coverKey'], res['data']['fileId']
    else:
        logger.info('文件校验出错 - ' + res['message'])
        return '', ''

def submit(session, api_ph, coverKey, fileId, caption = '', longitude = '', latitude = ''):
    data = {
        'caption': caption,
        'collectionId':"",
        'coverCenterX':"",
        'coverCenterY':"",
        'coverCropped':'false',
        'coverKey':coverKey,
        'coverTimeStamp':-1,
        'coverTitle':"",
        'coverTitleStyle':"",
        'coverType':1,
        'domain':"",
        'fileId':fileId,
        'kuaishou.web.cp.api_ph':api_ph,
        'latitude': latitude,
        'longitude': longitude,
        'movieId':"",
        'notifyResult':0,
        'photoStatus':1,
        'photoType':0,
        'pkCoverKey':"",
        'publishTime':0,
        'secondDomain':""
    }
    res = session.post('https://cp.kuaishou.com/rest/cp/works/v2/video/pc/submit', json=data, headers=headers).json()
    if res['result'] == 1:
        return 1
    else:
        logger.info('发布出错 - ' + res['message'])
    return 0

def publish(session, filePath, title, longitude='', latitude='', unlink = 1):
    logger.info('开始上传 - %s < %s' % (title, filePath))
    result = upvideo(session, filePath, title, longitude, latitude)
    if result == 1 and unlink == 1:
        os.remove(filePath)
        logger.info('上传完成 - %s > 缓存文件已删除' % title)
    return result

def traceexcept(type, value, trace):
    log = 'Trace#' + str(type) + '::' + str(value)
    while trace:
        log += os.linesep + ' File ' + str(trace.tb_frame.f_code.co_filename) + ', Line ' + str(trace.tb_lineno)
        trace = trace.tb_next
    logger.error(log)

if __name__ == '__main__':
    sys.excepthook = traceexcept
